#pragma once
#include"./Buffer.hpp"
#include<functional>
#include<memory>
#include<thread>
#include<atomic>
#include<condition_variable>

namespace zlog
{
  /* 异步工作器的工作模式 */
  enum class AsyncType
  {
      ASYNC_SAFE,
      ASYNC_UNSAFE
  };

  using Functor = std::function<void(Buffer&)>;
  class AsyncLooper
  {
    public:
      using ptr = shared_ptr<AsyncLooper>;
      AsyncLooper(const Functor& cb,AsyncType type = AsyncType::ASYNC_SAFE)//默认是安全模式
      :_stop(false),_thread(std::thread(&AsyncLooper::ThreadEntry,this)),_callback(cb),_type(type)
      {}

      ~AsyncLooper()
      {
         stop();
      }

      void stop()//停止异步工作器
      {
         //1.将标记位设置为true
         _stop = true;
         //2.唤醒所有在条件变量上等的异步工作线程
         _cond_con.notify_all();
         //3.线程等待
         _thread.join();
      }

      void push(const char* data,size_t len)
      {
          std::unique_lock<std::mutex> lock(_mtx);
          //1.外界选择是阻塞还是扩容
          ////安全模式：等到生产缓冲区中可写空间足够才不等(如果阻塞要等到异步线程处理完数据交换缓冲区才会被唤醒)
          if(_type == AsyncType::ASYNC_SAFE) 
            _cond_pro.wait(lock,[&](){return _pro_buf.WriteAbleSize() >= len;});
          //2.走到这里说明可以写入数据
           _pro_buf.push(data,len);
           //3.唤醒消费线程消费数据
           _cond_con.notify_one(); 
      }

    private:
      void ThreadEntry()
      {
        while(1)
        {
            //1.判断生产缓冲区中有没有数据,有则进行交换,无则阻塞
            {
               unique_lock<std::mutex> lock(_mtx);
               //在stop设置为true下,只有生产缓冲区中没有数据才能退出
               if(_stop == true && _pro_buf.empty() == true) break;
               //当前退出 或者 生产缓冲区中有数据才能继续往下走
               _cond_con.wait(lock,[&](){return _stop || _pro_buf.empty() == false;});
               //交换缓冲区中数据
               _con_buf.swap(_pro_buf);
               //开启安全模式才唤醒生产者线程
               if(_type == AsyncType::ASYNC_SAFE)
                 _cond_pro.notify_all();
            }
            //2.被唤醒之后调用回调处理数据
            _callback(_con_buf);
            //3.处理完数据初始化消费缓冲区,方便下次交换缓冲区的完全空闲
            _con_buf.Reset();
        }

      }
      Functor _callback; //具体回调由异步工作器使用者传入
      
     private:
       std::atomic<bool> _stop; //工作器停止标志
       Buffer _pro_buf; //生产缓冲区
       Buffer _con_buf; //消费缓冲区
       std::mutex _mtx;   
       std::condition_variable _cond_pro;//(生产)条件变量
       std::condition_variable _cond_con;//(消费)条件变量
       std::thread _thread;//异步工作器对应的工作线程
       AsyncType _type;
  };
}